#include "config.h"

#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <openssl/md5.h>
#include <openssl/aes.h>
#include <dirent.h>

#define DBG_SUBSYS S_LIBCLUSTER

#include "rpc_proto.h"
#include "ynet_rpc.h"
#include "net_global.h"
#include "fnotify.h"
#include "sdevent.h"
#include "timer.h"
#include "fence.h"
#include "mem_cache.h"
#include "cluster.h"
#include "node.h"
#include "nodectl.h"
#include "bh.h"
#include "main_loop.h"
#include "net_global.h"
#include "configure.h"
#include "lichconf.h"
#include "longtask.h"
#include "dbg.h"
#include "timer.h"
#include "diskmd.h"

#define NODECTL_PREFIX  SHM_ROOT"/nodectl"

static worker_handler_t diskctl_handler;

int nodectl_get(const char *key, char *value, const char *_default)
{
        int ret;
        char path[MAX_PATH_LEN];

        snprintf(path, MAX_PATH_LEN, "%s/%s", NODECTL_PREFIX, key);

        ret = _get_text(path, value, MAX_BUF_LEN);
        if (ret < 0) {
                ret = -ret;
                YASSERT(ret == ENOENT);
                ret = _set_text(path, _default, strlen(_default) + 1, O_CREAT | O_TRUNC);
                if (unlikely(ret))
                        GOTO(err_ret, ret);

                strcpy(value, _default);
        }

        return 0;
err_ret:
        return ret;
}

int nodectl_get_int(const char *key, int _default)
{
        int ret;
        char value[MAX_BUF_LEN], defvalue[MAX_BUF_LEN];

        sprintf(defvalue, "%d", _default);

        ret = nodectl_get(key, value, defvalue);
        if(unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return atoi(value);
err_ret:
        return _default;
}

int nodectl_set(const char *key, const char *value)
{
        int ret;
        char path[MAX_PATH_LEN];

        snprintf(path, MAX_PATH_LEN, "%s/%s", NODECTL_PREFIX, key);

        ret = _set_text(path, value, strlen(value) + 1, O_CREAT | O_TRUNC);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int nodectl_set2(const char *key, const char *value)
{
        int ret, fd;
        char path[MAX_PATH_LEN];

        snprintf(path, MAX_PATH_LEN, "%s/%s", NODECTL_PREFIX, key);

        ret = path_validate(path, 0, YLIB_DIRCREATE);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        fd = open(path, O_CREAT | O_WRONLY, 0644);
        if (fd < 0) {
                ret = errno;
                GOTO(err_ret, ret);
        }

        ret = _write(fd, value, strlen(value) + 1);
        if (ret < 0) {
                ret = -ret;
                GOTO(err_fd, ret);
        }

        ret = fsync(fd);
        if (ret < 0) {
                ret = errno;
                GOTO(err_fd, ret);
        }

        close(fd);
        return 0;
err_fd:
        close(fd);
err_ret:
        return ret;
}

int nodectl_register(const char *key, const char  *_default, fnotify_callback mod_callback,
                     fnotify_callback del_callback, void *context)
{
        int ret;
        char path[MAX_PATH_LEN];

        ret = nodectl_set(key, _default);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        snprintf(path, MAX_PATH_LEN, "%s/%s", NODECTL_PREFIX, key);
        ret = fnotify_register(path, mod_callback, del_callback, context);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int nodectl_unregister(const char *key)
{
        int ret;
        char path[MAX_PATH_LEN];

        snprintf(path, MAX_PATH_LEN, "%s/%s", NODECTL_PREFIX, key);
        ret = fnotify_unregister(path);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

void nodectl_unlink(const char *key)
{
        char path[MAX_PATH_LEN];

        snprintf(path, MAX_PATH_LEN, "%s/%s", NODECTL_PREFIX, key);

        if (_file_exist(path) == 0) {
                _unlink(path, "nodectl_unlink");
        }
}

/// @class diskctl
static int __diskctl_worker(void *args)
{
        int ret;
        (void) args;

        ret = diskmd_writeable();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        // TODO check disk is writable
        ret = timer1_settime(&diskctl_handler, USEC_PER_SEC * 5);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        DERROR("diskctl error ret (%d) %s\n", ret, strerror(ret));

        ret = timer1_settime(&diskctl_handler, USEC_PER_SEC);
        if (unlikely(ret)) {
                DERROR("settime error ret %d\n", ret);
                YASSERT(0);
        }

        return ret;
}

static int diskctl_start()
{
        int ret;

        ret = timer1_create(&diskctl_handler, "diskctl_file", __diskctl_worker, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = timer1_settime(&diskctl_handler, USEC_PER_SEC);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int diskctl_init()
{
        int ret;

        ret = diskctl_start();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int nodectl_init()
{
        int ret;

        DINFO("node ctl init\n");
        ret = diskctl_init();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}
